"""
TODO LIST:
expCube = GtApp('gtltcube', 'Likelihood') OK
addCubes = GtApp('gtltsum', 'Likelihood') OK
expMap = GtApp('gtexpmap', 'Likelihood') OK
diffResps = GtApp('gtdiffrsp', 'Likelihood') OK
like = GtApp('gtlike', 'Likelihood') NO
TsMap = GtApp('gttsmap', 'Likelihood') OK

counts_map = GtApp('gtbin', 'evtbin') OK
srcMaps = GtApp('gtsrcmaps', 'Likelihood') OK
model_map = GtApp('gtmodel', 'Likelihood') OK
gtexpcube2 = GtApp('gtexpcube2', 'Likelihood') OK

obsSim = GtApp('gtobssim', 'observationSim') NO

filter = GtApp('gtselect', 'dataSubselector') OK
maketime = GtApp('gtmktime', 'dataSubselector') OK
"""
from __future__ import print_function
import os
import pprint
import tempfile
import subprocess
from multiprocessing import Pool

import six
import numpy as np
import gt_apps as gts

from XmlTools import SMKernel
from . import MyNewLike

try:
    import pyfits as pf
except ImportError:
    from astropy.io import fits as pf

def _request_value(value, prompt='value', dtype=float, default=None):
    while True:
        try:
            if value is None:
                raise RuntimeError()
            else:
                if dtype == 'file':
                    _val = str(value)
                    if not os.path.isfile(_val):
                        raise RuntimeError()
                else:
                    _val = dtype(value)
        except:
            if default is None:
                print('Invalid %s(=%r)! dtype should be %s!' % (prompt, value, dtype))
                if six.PY2:
                    value = raw_input('%s = '%prompt)
                else:
                    value = input('%s = '%prompt)
            else:
                print('Invalid %s(=%r)! Set to default value %r!' % (prompt, value, default))
                _val = default
                break
        else:
            break
    return _val

def gtselect(infile, emin, emax, outfile,
             ra='INDEF', dec='INDEF', rad='INDEF',
             tmin='INDEF', tmax='INDEF',
             evclass='INDEF', evtype='INDEF',
             zmin=0., zmax=90.,
             phasemin=0, phasemax=1,
             convtype=-1):
    """
    Run gtselect!
    ra, dec, rad should be either num or INDEF
    tmin, tmax should be either num or INDEF
    convtype is only available for Pass 7
    zmin, evtype is not support for Pass 7
    """
    gts.filter['ra'] = ra
    gts.filter['dec'] = dec
    gts.filter['rad'] = rad
    gts.filter['emin'] = emin
    gts.filter['emax'] = emax
    gts.filter['zmax'] = zmax
    gts.filter['tmin'] = tmin
    gts.filter['tmax'] = tmax
    gts.filter['phasemin'] = phasemin
    gts.filter['phasemax'] = phasemax
    gts.filter['infile'] = infile
    gts.filter['outfile'] = outfile
    if 'evtype' in gts.filter.pars.keys():
        gts.filter['zmin'] = zmin
        gts.filter['evclass'] = evclass
        gts.filter['evtype'] = evtype
    else:
        gts.filter['convtype'] = convtype
    gts.filter.run()

def gtmktime(evfile, scfile, outfile,
             filters="(DATA_QUAL==1)&&(LAT_CONFIG==1)", roicut="no"):
    """
    run gtmktime
    The default parameters are for Pass 8.
    For pass 7, roicut=yes, and filters="(DATA_QUAL==1)&&(LAT_CONFIG==1)&&(ABS(ROCK_ANGLE)<52)"
    """
    gts.maketime['scfile'] = scfile
    gts.maketime['filter'] = filters
    gts.maketime['roicut'] = roicut
    gts.maketime['evfile'] = evfile
    gts.maketime['outfile'] = outfile
    gts.maketime.run()

def gtbin(evfile, algorithm, outfile,
          nxpix=None, nypix=None, binsz=None, proj='CAR', xref=None, yref=None, coordsys=None, axisrot=0., # for alg=cmap|ccube
          hpx_order=None, hpx_ordering_scheme='RING', hpx_ebin='yes', # for alg=healpix
          ebinalg='LOG', emin=None, emax=None, enumbins=None, ebinfile='NONE', # for alg=ccube|healpix
          tbinalg='LIN', tstart=None, tstop=None, dtime=None, tbinfile='NONE', # for alg=lc
          scfile='NONE'):
    """
    run gtbin
    """
    _alg = algorithm.lower()
    gts.evtbin['evfile'] = evfile
    gts.evtbin['algorithm'] = algorithm
    gts.evtbin['scfile'] = scfile
    gts.evtbin['outfile'] = outfile
    if _alg == 'cmap' or _alg == 'ccube':
        gts.evtbin['nxpix'] = _request_value(nxpix, prompt='nxpix', dtype=int)
        gts.evtbin['nypix'] = _request_value(nypix, prompt='nypix', dtype=int)
        gts.evtbin['binsz'] = _request_value(binsz, prompt='binsz', dtype=float)
        gts.evtbin['proj'] = proj
        gts.evtbin['xref'] = _request_value(xref, prompt='xref', dtype=float)
        gts.evtbin['yref'] = _request_value(yref, prompt='yref', dtype=float)
        gts.evtbin['coordsys'] = coordsys
        gts.evtbin['axisrot'] = axisrot
    elif _alg == 'healpix':
        gts.evtbin['hpx_order'] = _request_value(hpx_order, prompt='hpx_order', dtype=int)
        gts.evtbin['hpx_ordering_scheme'] = hpx_ordering_scheme
        gts.evtbin['hpx_ebin'] = hpx_ebin
    elif _alg == 'lc':
        gts.evtbin['tbinalg'] = tbinalg
        gts.evtbin['tstart'] = _request_value(tstart, 'tstart', float)
        gts.evtbin['tstop'] = _request_value(tstop, 'tstop', float)
        gts.evtbin['dtime'] = _request_value(dtime, 'dtime', float)
        if tbinalg.lower() == 'file':
            gts.evtbin['tbinfile'] = tbinfile
    else:
        raise IOError('Invalid algorithms %s!' % algorithm)

    if _alg == 'ccube' or (_alg == 'healpix' and hpx_ebin == 'yes'):
        gts.evtbin['ebinalg'] = ebinalg
        gts.evtbin['emin'] = _request_value(emin, 'emin', float)
        gts.evtbin['emax'] = _request_value(emax, 'emax', float)
        gts.evtbin['enumbins'] = _request_value(enumbins, 'enumbins', int)
        if ebinalg.lower() == 'file':
            gts.evtbin['ebinfile'] = ebinfile

    gts.evtbin.run()

def gtltcube(evfile, scfile, zmax, outfile,
             zmin=0., binsz=1., dcostheta=0.025,
             phibins=0):
    """
    run gtltcube
    If roicut=yes, zmax=180, else set to the same value as gtselect.
    """
    gts.expCube['evfile'] = evfile
    gts.expCube['scfile'] = scfile
    gts.expCube['zmax'] = zmax
    gts.expCube['outfile'] = outfile
    gts.expCube['zmin'] = zmin
    gts.expCube['dcostheta'] = dcostheta
    gts.expCube['binsz'] = binsz
    gts.expCube['phibins'] = phibins
    gts.expCube.run()

def gtltsum(infile, outfile,
            infile2=None):
    """
    run gtltsum
    """
    gts.addCubes['infile1'] = infile
    gts.addCubes['outfile'] = outfile
    if not infile.startswith('@'):
        gts.addCubes['infile2'] = _request_value(infile2, 'infile2', 'file')
    gts.addCubes.run()

def gtexpcube2(ltcube, irfs, xref, yref, coordsys, nxpix, nypix, binsz, emin, emax, enumbins, outfile,
               evtype='INDEF', axisrot=0, proj='CAR', ebinalg='LOG', ebinfile='NONE', bincalc='EDGE'):
    """
    run gtexpcube2
    """
    gts.gtexpcube2['infile'] = ltcube
    gts.gtexpcube2['cmap'] = 'none'
    gts.gtexpcube2['outfile'] = outfile
    gts.gtexpcube2['irfs'] = irfs
    gts.gtexpcube2['xref'] = xref
    gts.gtexpcube2['yref'] = yref
    gts.gtexpcube2['nxpix'] = nxpix
    gts.gtexpcube2['nypix'] = nypix
    gts.gtexpcube2['binsz'] = binsz
    gts.gtexpcube2['coordsys'] = coordsys
    gts.gtexpcube2['axisrot'] = axisrot
    gts.gtexpcube2['proj'] = proj
    gts.gtexpcube2['ebinalg'] = ebinalg
    gts.gtexpcube2['emin'] = emin
    gts.gtexpcube2['emax'] = emax
    gts.gtexpcube2['enumbins'] = enumbins
    gts.gtexpcube2['bincalc'] = bincalc
    if 'evtype' in gts.expcube2.pars.keys():
        gts.gtexpcube2['evtype'] = evtype
    if ebinalg.lower() == 'file':
        gts.gtexpcube2['ebinfile'] = ebinfile
    gts.gtexpcube2.run()

def gtsrcmaps(cmap, scfile, ltcube, expcube2, srcmdl, irfs, outfile,
              evtype='INDEF', rfactor=4, ptsrc='yes', copyall='no',
              psfcorr='yes', convol='yes'):
    """
    run gtsrcmaps
    """
    gts.srcMaps['cmap'] = cmap
    gts.srcMaps['scfile'] = scfile
    gts.srcMaps['srcmdl'] = srcmdl
    gts.srcMaps['expcube'] = ltcube
    gts.srcMaps['bexpmap'] = expcube2
    gts.srcMaps['irfs'] = irfs
    gts.srcMaps['outfile'] = outfile
    gts.srcMaps['rfactor'] = rfactor
    gts.srcMaps['ptsrc'] = ptsrc
    gts.srcMaps['copyall'] = copyall
    gts.srcMaps['psfcorr'] = psfcorr
    gts.srcMaps['convol'] = convol
    if 'evtype' in gts.srcMaps.pars.keys():
        gts.srcMaps['evtype'] = evtype
    gts.srcMaps.run()

def gtmodel(srcmaps, srcmdl, ltcube, expcube2, irfs, outfile,
            evtype='INDEF', outtype='cmap'):
    """
    run gtmodel
    """
    gts.model_map['srcmaps'] = srcmaps
    gts.model_map['srcmdl'] = srcmdl
    gts.model_map['outfile'] = outfile
    gts.model_map['expcube'] = ltcube
    gts.model_map['bexpmap'] = expcube2
    gts.model_map['irfs'] = irfs
    gts.model_map['outtype'] = outtype
    if 'evtype' in gts.model_map.pars.keys():
        gts.model_map['evtype'] = evtype
    gts.model_map.run()

def gtdiffrsp(evfile, scfile, srcmdl, outfile,
              irfs='CALDB', evclass='INDEF', evtype='INDEF'):
    """
    run gtdiffrsp
    """
    subprocess.call('cp %s %s' % (evfile, outfile), shell=True)
    gts.diffResps['evfile'] = outfile
    gts.diffResps['scfile'] = scfile
    gts.diffResps['srcmdl'] = srcmdl
    gts.diffResps['irfs'] = irfs
    gts.diffResps['evclass'] = evclass
    if 'evtype' in gts.diffResps.pars.keys():
        gts.diffResps['evtype'] = evtype
    gts.diffResps.run()

def gtexpmap(evfile, scfile, ltcube, evtype, nlong, nlat, srcrad, nenergies, outfile,
             irfs='CALDB'):
    """
    run gtexpmap
    """
    gts.expMap['evfile'] = evfile
    gts.expMap['scfile'] = scfile
    gts.expMap['expcube'] = ltcube
    gts.expMap['irfs'] = irfs
    gts.expMap['nlong'] = nlong
    gts.expMap['nlat'] = nlat
    gts.expMap['srcrad'] = srcrad
    gts.expMap['nenergies'] = nenergies
    gts.expMap['outfile'] = outfile
    if 'evtype' in gts.expMap.pars.keys():
        gts.expMap['evtype'] = evtype
    gts.expMap.run()

def gttsmap(statistic, srcmdl, ltcube, evtype, nxpix, nypix, binsz, xref, yref, coordsys, outfile,
            evfile=None, scfile=None, expmap=None, # for unbinned
            cmap=None, expcube2=None, # for binned
            optimizer='MINUIT', ftol=1.e-3, toltype='ABS',
            irfs='CALDB', proj='CAR'):
    """
    run gttsmap
    """
    gts.TsMap['srcmdl'] = srcmdl
    gts.TsMap['statistic'] = statistic
    gts.TsMap['expcube'] = ltcube
    gts.TsMap['irfs'] = irfs
    gts.TsMap['optimizer'] = optimizer
    gts.TsMap['ftol'] = ftol
    gts.TsMap['toltype'] = toltype
    gts.TsMap['nxpix'] = nxpix
    gts.TsMap['nypix'] = nypix
    gts.TsMap['binsz'] = binsz
    gts.TsMap['proj'] = proj
    gts.TsMap['xref'] = xref
    gts.TsMap['yref'] = yref
    gts.TsMap['coordsys'] = coordsys
    gts.TsMap['outfile'] = outfile
    _stat = statistic.lower()
    if _stat == 'unbinned':
        gts.TsMap['evfile'] = _request_value(evfile, 'evfile', 'file')
        gts.TsMap['scfile'] = _request_value(scfile, 'scfile', 'file')
        gts.TsMap['expmap'] = _request_value(expmap, 'expmap', 'file')
    elif _stat == 'binned':
        gts.TsMap['cmap'] = _request_value(cmap, 'cmap', 'file')
        gts.TsMap['bexpmap'] = _request_value(expcube2, 'expcube2', 'file')
    else:
        raise IOError('UNKNOWN statistic %s!'%statistic)
    if 'evtype' in gts.TsMap.pars.keys():
        gts.TsMap['evtype'] = evtype
    gts.TsMap.run()

def gtpsf(ltcube, irfs, evtype, ra, dec, emin, emax, nenergies, outfile,
          thetamax=90., ntheta=300):
    """
    run gtpsf
    E in [MeV], Emin should be larger or equal to 18 MeV, while Emax should be smaller or equal to 5.6e6 MeV.
    evtype = 1(front), 2(back)
    nenergies should be nbin + 1
    """
    cmdline = ['gtpsf', 'expcube=%s' % ltcube, 'outfile=%s' % outfile,
               'ra=%.12g' % ra, 'dec=%.12g' % dec,
               'emin=%.12g' % emin, 'emax=%.12g' % emax, 'nenergies=%i' % nenergies,
               'thetamax=%.12g' % thetamax, 'ntheta=%i' % ntheta]

    if irfs.startswith('P8'):
        cmdline.append('irfs=%s' % irfs)
        cmdline.append('evtype=%i' % evtype)
    else:
        if not '::' in irfs:
            if evtype == 1:
                cmdline.append('irfs=%s::FRONT' % irfs)
            elif evtype == 2:
                cmdline.append('irfs=%s::BACK' % irfs)
            else:
                cmdline.append('irfs=%s' % irfs)
        else:
            cmdline.append('irfs=%s' % irfs)

    print(' '.join(cmdline))
    subprocess.call(cmdline)

# MPI for gtpsf
def _gtpsf_mp_drv(kwds):
    return gtpsf(**kwds)

def gtfindsrc(evfile, scfile, ltcube, expmap, srcmdl, irfs, evtype, outmdl, outdata, target,
              optimizer='MINUIT', ftol=1.e-2, toltype='ABS', atol=1.e-2):
    """
    run gtfindsrc
    outdata: the gtfindsrc fit information and results
    ftol: Relative fit tolerance; atol: Convergence tolerance for positional fit
    """
    subprocess.call('cp %s %s' % (srcmdl, outmdl), shell=True)

    cmdline = ['gtfindsrc', 'evfile=%s'%evfile, 'scfile=%s'%scfile, 'srcmdl=%s'%outmdl,
               'expcube=%s'%ltcube, 'expmap=%s'%expmap, 'outfile=%s'%outdata,
               'target="%s"'%target, 'optimizer=%s'%optimizer,
               'ftol=%.12g'%ftol, 'toltype=%s'%toltype, 'atol=%.12g'%atol]

    if irfs.startswith('P8'):
        cmdline.append('irfs=%s' % irfs)
        cmdline.append('evtype=%i' % evtype)
    else:
        if not '::' in irfs:
            if evtype == 1:
                cmdline.append('irfs=%s::FRONT' % irfs)
            elif evtype == 2:
                cmdline.append('irfs=%s::BACK' % irfs)
            else:
                cmdline.append('irfs=%s' % irfs)
        else:
            cmdline.append('irfs=%s' % irfs)

    print(' '.join(cmdline))
    _output = subprocess.check_output(' '.join(cmdline), shell=True)
    if isinstance(_output, bytes):
        _output = _output.decode('ascii')

    retdata = {}
    fh = open(outdata, 'a+')
    fh.seek(0)
    for line in fh:
        if line.startswith('initial'):
            line_split = line.strip().split(':')[-1].split()
            retdata['ra_init'] = float(line_split[0])
            retdata['dec_init'] = float(line_split[1])
            retdata['loglike_init'] = -float(line_split[2])
        elif line.startswith('final'):
            line_split = line.strip().split(':')[-1].split()
            retdata['ra_final'] = float(line_split[0])
            retdata['dec_final'] = float(line_split[1])
            retdata['loglike_final'] = -float(line_split[2])
        elif line.startswith('angular'):
            line_split = line.strip().split(':')[-1].split()
            retdata['angsep'] = float(line_split[0])
    fh.write(_output)
    fh.write('\n')
    fh.close()

    output = _output.split('\n')
    for line in output:
        if line.startswith('Error'):
            line_split = line.strip().split(':')[-1].split()
            retdata['error_radius'] = float(line_split[0])

    return retdata


# MPI for gtfindsrc
def _gtfindsrc_mp_drv(kwds):
    return gtfindsrc(**kwds)

def gtfindsrc_mp(evfile, scfile, ltcube, expmap, srcmdl, irfs, evtype, outmdl, outdata, targets,
                 optimizer='MINUIT', ftol=1.e-2, toltype='ABS', atol=1.e-2,
                 npass=10, max_njobs=10, finaldata='gtfindsrc_results.dat',
                 save_region=True, regionfile='ROI_newpts_fdsrc.reg'):
    """
    npass=4 represent we will run 4 time gtfindsrc
    outdata: the gtfindsrc fit information and results
    targets: a list or a file contains source names. When run with cli, it can only be a file
    ftol: Relative fit tolerance; atol: Convergence tolerance for positional fit
    """
    if isinstance(targets, str):
        fh = open(targets, 'r')
        _targets = [tgt.strip() for tgt in fh]
        fh.close()
    else:
        _targets = [targets]

    njobs = len(_targets)
    if njobs > max_njobs:
        raise RuntimeError('Too many targets %s!'%_targets)

    _srcmdl = srcmdl
    _outmdl_pfx = '.'.join(outmdl.split('.')[:-1])
    _outdata_pfx = '.'.join(outdata.split('.')[:-1])

    pool = Pool(processes=njobs)
    for ipass in range(npass):
        isbreak = True

        poolargs = []
        for target in _targets:
            _target = '_'.join(target.split())
            _outmdl = _outmdl_pfx + '_pass%i_%s.xml' % (ipass+1, _target)
            _outdata = _outdata_pfx + '_pass%i_%s.dat' % (ipass+1, _target)
            kwds = {'evfile' : evfile, 'scfile' : scfile, 'ltcube' : ltcube, 'expmap' : expmap,
                    'srcmdl' : _srcmdl, 'irfs' : irfs, 'evtype' : evtype,
                    'outmdl' : _outmdl, 'outdata' : _outdata, 'target' : target,
                    'optimizer' : optimizer, 'ftol' : ftol, 'toltype' : toltype, 'atol' : atol}
            poolargs.append(kwds)

        print('PASS%i:' % (ipass+1))
        allretdata = pool.map(_gtfindsrc_mp_drv, poolargs)

        x = SMKernel.SourceModel(_srcmdl, raiseErr=True)
        for target, retdata in zip(_targets, allretdata):
            x[target].spatialModel.RA.value = retdata['ra_final']
            x[target].spatialModel.DEC.value = retdata['dec_final']
            isbreak *= (retdata['angsep'] == 0.)
        _srcmdl = _outmdl_pfx + '_pass%i.xml' % (ipass+1)
        x.writeTo(_srcmdl)

        if isbreak:
            break

    subprocess.call('cp %s %s' % (_srcmdl, outmdl), shell=True)

    regobj = SMKernel.MyRegion()
    fh = open(finaldata, 'w')
    fh.write('#target\tRA[deg]\tDec[deg]\terror_radius_1sigma[deg]\tangsep[deg]\n')
    for target, retdata in zip(_targets, allretdata):
        final_ra = retdata['ra_final']
        final_dec = retdata['dec_final']
        final_errrad = retdata['error_radius']
        final_angsep = retdata['angsep']

        fh.write('%s\t%f\t%f\t%f\t%f\n' % (target, final_ra, final_dec, final_errrad, final_angsep))

        regobj.addCircle(target, final_ra, final_dec, final_errrad,
                         color='green', coord='CEL')
    fh.close()

    if save_region:
        regobj.writeTo(regionfile)


# MPI for gtltcube
def _gtltcube_mp_drv(args):
    """
    The driver of gtltcube (SHOULD NOT BE USED ANYTIME!!)
    evfile, tmin, tmax, scfile, zmax, zmin, binsz, dcostheta, phibins = args
    """
    evfile, tmin, tmax, scfile, zmax, zmin, binsz, dcostheta, phibins = args
    evfile0 = tempfile.NamedTemporaryFile(prefix='gti_', suffix=".fits")
    ltcubeh, ltcube0 = tempfile.mkstemp(prefix='ltcube_', suffix=".fits")

    gtselect(infile=evfile, emin=0, emax=1e6, outfile=evfile0.name, tmin=tmin, tmax=tmax, zmin=zmin, zmax=zmax)
    gtltcube(evfile=evfile0.name, scfile=scfile, zmax=zmax, outfile=ltcube0,
             zmin=zmin, binsz=binsz, dcostheta=dcostheta, phibins=phibins)
    evfile0.close()
    return ltcube0

def gtltcube_mp(njobs, evfile, scfile, zmax, outfile,
                zmin=0., binsz=1., dcostheta=0.025,
                phibins=0):
    _njobs = _request_value(njobs, 'njobs', int)
    if _njobs < 2:
        gtltcube(evfile=evfile, scfile=scfile, zmax=zmax, outfile=outfile,
                 zmin=zmin, binsz=binsz, dcostheta=dcostheta,
                 phibins=phibins)
    else:
        evfits = pf.open(evfile, memmap=True)
        evheader = evfits['EVENTS'].header
        tmin = evheader['TSTART']
        tmax = evheader['TSTOP']
        evfits.close()
        tsplit = np.linspace(tmin, tmax, njobs+1)

        input_pars = []
        for ijob in range(njobs):
            mytmin, mytmax = tsplit[ijob:ijob+2]
            input_pars.append((evfile, mytmin, mytmax, scfile, zmax, zmin, binsz, dcostheta, phibins))

        pool = Pool(processes=njobs)
        ltcubefiles = pool.map(_gtltcube_mp_drv, input_pars)

        lcfile = tempfile.NamedTemporaryFile(prefix='ltcubes_', suffix=".lst")
        lchand = open(lcfile.name, 'w')
        lchand.write('\n'.join(ltcubefiles))
        lchand.close()

        gtltsum('@'+lcfile.name, outfile)
        lcfile.close()

        for ltcube0 in ltcubefiles:
            os.remove(ltcube0)


# generalized object
_Char2Evclass_P8 = {'TRANSIENT020' : 16, 'TRANSIENT010' : 64, 'SOURCE' : 128,
                    'CLEAN' : 256, 'ULTRACLEAN' : 512, 'ULTRACLEANVETO' : 1024}

class _MyAnalysisBase(object):
    """
    Only for pass 8
    DO NOT CHANGE ANY OF THE VARIABLES STARTED WITH _!!!
    """

    def __init__(self, objname, irfs, evtype, saveFolder=None):
        self._objname = str(objname)
        self._irfs = str(irfs)
        self._evtype = int(evtype)
        for key, value in _Char2Evclass_P8.items():
            if '_%s_'%key in irfs:
                self._evclass = value
                break
        else:
            raise IOError('Can not get evclass from irfs %s!' % irfs)

        if saveFolder is None:
            self.save_folder = '.'
        else:
            if os.path.exists(saveFolder):
                if saveFolder.endswith('/'):
                    self.save_folder = saveFolder[:-1]
                else:
                    self.save_folder = saveFolder
            else:
                raise IOError('SaveFolder %s does not exist!' % saveFolder)

        self.parameters = {'objname' : objname,
                           'evtype' : self._evtype, 'evclass' : self._evclass, 'irfs' : self._irfs,
                           'evfile' : None, 'scfile' : None,
                           'fltfile' : None, 'gtifile' : None, 'ccubefile' : None, 'ltcubefile' : None,
                           'ra' : None, 'dec' : None, 'glon' : None, 'glat' : None, 'rad' : None,
                           'emin' : None, 'emax' : None, 'bemin' : None, 'bemax' : None,
                           'tmin' : None, 'tmax' : None, 'zmin' : None, 'zmax' : None,
                           'phasemin' : None, 'phasemax' : None,
                           'filters' : None, 'roicut' : None,
                           'coordsys' : None, 'nxpix' : None, 'nypix' : None, 'proj' : None,
                           'ltcube_binsz' : None, 'ltcube_dcostheta' : None, 'ltcube_phibins' : None,
                           'srcmdl' : None
                          }

    def gtselect(self, evfile, ra, dec, rad, emin, emax, tmin, tmax, zmax, fltfile=None,
                 zmin=0., phasemin=0, phasemax=1):
        """
        ra, dec, rad can NOT be INDEF!
        tmin, tmax can be INDEF!
        """
        _fltfile = fltfile or '%s/%s_filtered.fits' % (self.save_folder, self._objname)
        gtselect(infile=evfile, emin=emin, emax=emax, outfile=_fltfile,
                 ra=ra, dec=dec, rad=rad, tmin=tmin, tmax=tmax,
                 evclass=self._evclass, evtype=self._evtype,
                 zmin=zmin, zmax=zmax, phasemin=phasemin, phasemax=phasemax)

        self.parameters['evfile'] = evfile
        self.parameters['fltfile'] = _fltfile

        self.parameters['ra'] = ra
        self.parameters['dec'] = dec
        self.parameters['rad'] = rad
        self.parameters['emin'] = emin
        self.parameters['emax'] = emax
        self.parameters['tmin'] = tmin
        self.parameters['tmax'] = tmax
        self.parameters['zmin'] = zmin
        self.parameters['zmax'] = zmax
        self.parameters['phasemin'] = phasemin
        self.parameters['phasemax'] = phasemax

        # TODO: some lines to change ra, dec to lon, lat

    def gtmktime(self, scfile,
                 filters="(DATA_QUAL==1)&&(LAT_CONFIG==1)", roicut="no", gtifile=None,
                 fltfile=None):
        if (self.parameters['fltfile'] is None) or (not fltfile is None):
            self.parameters['fltfile'] = _request_value(fltfile, 'fltfile', 'file')

        _gtifile = gtifile or '%s/%s_gti.fits' % (self.save_folder, self._objname)
        gtmktime(evfile=self.parameters['fltfile'], scfile=scfile, outfile=_gtifile,
                 filters=filters, roicut=roicut)

        self.parameters['scfile'] = scfile
        self.parameters['gtifile'] = _gtifile
        self.parameters['filters'] = filters
        self.parameters['roicut'] = roicut

    def gtbin(self, nxpix, nypix, binsz, coordsys, enumbins, proj='CAR', ccubefile=None,
              gtifile=None, ra=None, dec=None, glon=None, glat=None, bemin=None, bemax=None):
        if (self.parameters['gtifile'] is None) or (not gtifile is None):
            self.parameters['gtifile'] = _request_value(gtifile, 'gtifile', 'file')

        _coord = coordsys.lower()
        if _coord == 'cel':
            if (self.parameters['ra'] is None) or (not ra is None):
                self.parameters['ra'] = _request_value(ra, 'ra', float)
            if (self.parameters['dec'] is None) or (not dec is None):
                self.parameters['dec'] = _request_value(dec, 'dec', float)
            _xref, _yref = self.parameters['ra'], self.parameters['dec']
        elif _coord == 'gal':
            if (self.parameters['glon'] is None) or (not glon is None):
                self.parameters['glon'] = _request_value(glon, 'glon', float)
            if (self.parameters['glat'] is None) or (not glat is None):
                self.parameters['glat'] = _request_value(glat, 'glat', float)
            _xref, _yref = self.parameters['glon'], self.parameters['glat']
        else:
            raise IOError('Invalid coordsys %s!' % coordsys)

        if bemin is None:
            if self.parameters['emin'] is None:
                self.parameters['emin'] = _request_value(bemin, 'bemin', float)
            self.parameters['bemin'] = self.parameters['emin']
        else:
            self.parameters['bemin'] = _request_value(bemin, 'bemin', float)

        if bemax is None:
            if self.parameters['emax'] is None:
                self.parameters['emax'] = _request_value(bemax, 'bemax', float)
            self.parameters['bemax'] = self.parameters['emax']
        else:
            self.parameters['bemax'] = _request_value(bemax, 'bemax', float)

        _ccubefile = ccubefile or '%s/%s_CCUBE.fits' % (self.save_folder, self._objname)
        gtbin(evfile=self.parameters['gtifile'], algorithm='CCUBE', outfile=_ccubefile,
              nxpix=nxpix, nypix=nypix, binsz=binsz, proj=proj, xref=_xref, yref=_yref, coordsys=coordsys, axisrot=0.,
              ebinalg='LOG', emin=self.parameters['bemin'], emax=self.parameters['bemax'], enumbins=enumbins)

        self.parameters['ccubefile'] = _ccubefile
        self.parameters['coordsys'] = coordsys
        self.parameters['nxpix'] = nxpix
        self.parameters['nypix'] = nypix
        self.parameters['coordsys'] = coordsys
        self.parameters['proj'] = proj

    def gtltcube(self, njobs=1, binsz=0.1, dcostheta=0.025, phibins=0, ltcubefile=None,
                 gtifile=None, scfile=None, zmin=None, zmax=None):
        if (self.parameters['gtifile'] is None) or (not gtifile is None):
            self.parameters['gtifile'] = _request_value(gtifile, 'gtifile', 'file')

        if (self.parameters['scfile'] is None) or (not scfile is None):
            self.parameters['scfile'] = _request_value(scfile, 'scfile', 'file')

        if (self.parameters['zmax'] is None) or (not zmax is None):
            if self.parameters['roicut'] == 'yes':
                self.parameters['zmax'] = 180.
            else:
                self.parameters['zmax'] = _request_value(zmax, 'zmax', float)

        if (self.parameters['zmin'] is None) or (not zmin is None):
            self.parameters['zmin'] = zmin or 0.

        _ltcubefile = ltcubefile or '%s/%s_ltcube.fits' % (self.save_folder, self._objname)
        if njobs > 1:
            raise NotImplementedError()
            gtltcube_mp(njobs=njobs, evfile=self.parameters['gtifile'], scfile=self.parameters['scfile'],
                        zmax=self.parameters['zmax'], outfile=_ltcubefile,
                        zmin=self.parameters['zmin'], binsz=binsz, dcostheta=dcostheta,
                        phibins=phibins)
        else:
            gtltcube(evfile=self.parameters['gtifile'], scfile=self.parameters['scfile'],
                     zmax=self.parameters['zmax'], outfile=_ltcubefile,
                     zmin=self.parameters['zmin'], binsz=binsz, dcostheta=dcostheta,
                     phibins=phibins)

        self.parameters['ltcubefile'] = _ltcubefile
        self.parameters['ltcube_binsz'] = binsz
        self.parameters['ltcube_dcostheta'] = dcostheta
        self.parameters['ltcube_phibins'] = phibins

    def __repr__(self):
        return str(self.parameters)

    def __getitem__(self, key):
        return self.parameters[key]

    def write_parameters(self, outfile='myparams.dat'):
        fh = open(outfile, 'w')
        pprint.pprint(self.parameters, stream=fh)
        fh.close()


class BinnedAnalysis(_MyAnalysisBase):

    def __init__(self, objname, irfs, evtype, saveFolder=None):
        _MyAnalysisBase.__init__(self, objname, irfs, evtype, saveFolder=saveFolder)

        binned_parameters = {'expcubefile' : None, 'srcmapfile' : None,
                             'expc_nxpix' : None, 'expc_nypix' : None, 'expc_binsz' : None,
                            }
        self.parameters.update(binned_parameters)

    def gtexpcube2(self, expc_nxpix, expc_nypix, expc_binsz, expcubefile=None,
                   ltcubefile=None, xref=None, yref=None, coordsys=None,
                   bemin=None, bemax=None, enumbins=None, proj=None,
                   bincalc='EDGE'):
        if (self.parameters['ltcubefile'] is None) or (not ltcubefile is None):
            self.parameters['ltcubefile'] = _request_value(ltcubefile, 'ltcubefile', 'file')

        if (self.parameters['proj'] is None) or (not proj is None):
            self.parameters['proj'] = _request_value(proj, 'proj', str)

        if (self.parameters['coordsys'] is None) or (not coordsys is None):
            self.parameters['coordsys'] = _request_value(coordsys, 'coordsys', str)

        _coord = self.parameters['coordsys'].lower()
        if _coord == 'cel':
            if (self.parameters['ra'] is None) or (not xref is None):
                self.parameters['ra'] = _request_value(xref, 'ra', float)
            if (self.parameters['dec'] is None) or (not yref is None):
                self.parameters['dec'] = _request_value(yref, 'dec', float)
            _xref, _yref = self.parameters['ra'], self.parameters['dec']
        elif _coord == 'gal':
            if (self.parameters['glon'] is None) or (not xref is None):
                self.parameters['glon'] = _request_value(xref, 'glon', float)
            if (self.parameters['glat'] is None) or (not yref is None):
                self.parameters['glat'] = _request_value(yref, 'glat', float)
            _xref, _yref = self.parameters['glon'], self.parameters['glat']
        else:
            raise IOError('Invalid coordsys %s!' % coordsys)

        if (self.parameters['bemin'] is None) or (not bemin is None):
            self.parameters['bemin'] = _request_value(bemin, 'bemin', float)

        if (self.parameters['bemax'] is None) or (not bemax is None):
            self.parameters['bemax'] = _request_value(bemax, 'bemax', float)

        if (self.parameters['enumbins'] is None) or (not enumbins is None):
            self.parameters['enumbins'] = _request_value(enumbins, 'enumbins', int)

        _expcubefile = expcubefile or '%s/%s_expcube.fits' % (self.save_folder, self._objname)
        gtexpcube2(ltcube=self.parameters['ltcubefile'], irfs=self._irfs, evtype=self._evtype,
                   xref=_xref, yref=_yref, coordsys=self.parameters['coordsys'],
                   nxpix=expc_nxpix, nypix=expc_nypix, binsz=expc_binsz,
                   emin=self.parameters['bemin'], emax=self.parameters['bemax'],
                   enumbins=self.parameters['enumbins'], outfile=_expcubefile,
                   axisrot=0, proj=self.parameters['proj'], ebinalg='LOG', ebinfile='NONE',
                   bincalc=bincalc)

        self.parameters['expcubefile'] = _expcubefile
        self.parameters['expc_nxpix'] = expc_nxpix
        self.parameters['expc_nypix'] = expc_nypix
        self.parameters['expc_binsz'] = expc_binsz

    def gtsrcmaps(self, srcmdl, ptsrc='yes', srcmapfile=None,
                  copyall='no', rfactor=4, psfcorr='yes', convol='yes',
                  ccubefile=None, scfile=None, ltcubefile=None, expcubefile=None):
        if (self.parameters['ccubefile'] is None) or (not ccubefile is None):
            self.parameters['ccubefile'] = _request_value(ccubefile, 'ccubefile', 'file')

        if (self.parameters['scfile'] is None) or (not scfile is None):
            self.parameters['scfile'] = _request_value(scfile, 'scfile', 'file')

        if (self.parameters['ltcubefile'] is None) or (not ltcubefile is None):
            self.parameters['ltcubefile'] = _request_value(ltcubefile, 'ltcubefile', 'file')

        if (self.parameters['expcubefile'] is None) or (not expcubefile is None):
            self.parameters['expcubefile'] = _request_value(expcubefile, 'expcubefile', 'file')

        _srcmapfile = srcmapfile or '%s/%s_srcmaps.fits' % (self.save_folder, self._objname)
        gtsrcmaps(cmap=self.parameters['ccubefile'], scfile=self.parameters['scfile'],
                  ltcube=self.parameters['ltcubefile'], expcube2=self.parameters['expcubefile'],
                  srcmdl=srcmdl, irfs=self._irfs, evtype=self._evtype, outfile=_srcmapfile,
                  rfactor=rfactor, ptsrc=ptsrc, copyall=copyall,
                  psfcorr=psfcorr, convol=convol)

        self.parameters['srcmdl'] = srcmdl
        self.parameters['srcmapfile'] = _srcmapfile

    def pylike(self, srcmapfile=None, ltcubefile=None, expcubefile=None, srcmdl=None, **kwds):
        if (self.parameters['srcmapfile'] is None) or (not srcmapfile is None):
            self.parameters['srcmapfile'] = _request_value(srcmapfile, 'srcmapfile', 'file')

        if (self.parameters['ltcubefile'] is None) or (not ltcubefile is None):
            self.parameters['ltcubefile'] = _request_value(ltcubefile, 'ltcubefile', 'file')

        if (self.parameters['expcubefile'] is None) or (not expcubefile is None):
            self.parameters['expcubefile'] = _request_value(expcubefile, 'expcubefile', 'file')

        if (self.parameters['srcmdl'] is None) or (not srcmdl is None):
            self.parameters['srcmdl'] = _request_value(srcmdl, 'srcmdl', 'file')

        MyNewLike.binLikeCLI(srcMaps=self.parameters['srcmapfile'], expCube=self.parameters['ltcubefile'],
                             binnedExpMap=self.parameters['expcubefile'], srcModel=self.parameters['srcmdl'],
                             irfs=self._irfs, **kwds)


class UnbinnedAnalysis(_MyAnalysisBase):

    def __init__(self, objname, irfs, evtype, saveFolder=None):
        _MyAnalysisBase.__init__(self, objname, irfs, evtype, saveFolder=saveFolder)

        unbinned_parameters = {'diffrspfile' : None,
                               'epm_nlong' : None, 'epm_nlat' : None, 'epm_srcrad' : None, 'epm_nenergies' : None,
                              }
        self.parameters.update(unbinned_parameters)

    def gtdiffrsp(self, srcmdl, diffrspfile=None,
                  gtifile=None, scfile=None):
        if (self.parameters['gtifile'] is None) or (not gtifile is None):
            self.parameters['gtifile'] = _request_value(gtifile, 'gtifile', 'file')

        if (self.parameters['scfile'] is None) or (not scfile is None):
            self.parameters['scfile'] = _request_value(scfile, 'scfile', 'file')

        _diffrspfile = diffrspfile or '%s/%s_gti_diffrsp.fits' % (self.save_folder, self._objname)
        gtdiffrsp(evfile=self.parameters['gtifile'], scfile=self.parameters['scfile'],
                  srcmdl=srcmdl, outfile=_diffrspfile,
                  irfs=self._irfs, evclass=self._evclass, evtype=self._evtype)

        self.parameters['srcmdl'] = srcmdl
        self.parameters['diffrspfile'] = _diffrspfile

    def gtexpmap(self, nlong, nlat, srcrad, nenergies, expmapfile=None,
                 diffrspfile=None, scfile=None, ltcubefile=None):
        if (self.parameters['diffrspfile'] is None) or (not diffrspfile is None):
            if (self.parameters['gtifile'] is None) or (not diffrspfile is None):
                self.parameters['diffrspfile'] = _request_value(diffrspfile, 'diffrspfile', 'file')
            else:
                self.parameters['diffrspfile'] = self.parameters['gtifile']

        if (self.parameters['scfile'] is None) or (not scfile is None):
            self.parameters['scfile'] = _request_value(scfile, 'scfile', 'file')

        if (self.parameters['ltcubefile'] is None) or (not ltcubefile is None):
            self.parameters['ltcubefile'] = _request_value(ltcubefile, 'ltcubefile', 'file')

        _expmapfile = expmapfile or '%s/%s_expmap.fits' % (self.save_folder, self._objname)
        gtexpmap(evfile=self.parameters['gtifile'], scfile=self.parameters['scfile'],
                 ltcube=self.parameters['ltcubefile'], outfile=_expmapfile,
                 evtype=self._evtype, nlong=nlong, nlat=nlat, srcrad=srcrad, nenergies=nenergies,
                 irfs=self._irfs)

        self.parameters['expmapfile'] = _expmapfile
        self.parameters['epm_nlong'] = nlong
        self.parameters['epm_nlat'] = nlat
        self.parameters['epm_srcrad'] = srcrad
        self.parameters['epm_nenergies'] = nenergies

    def pylike(self, diffrspfile=None, scfile=None, expmapfile=None, ltcubefile=None,
               srcmdl=None, **kwds):
        if (self.parameters['diffrspfile'] is None) or (not diffrspfile is None):
            self.parameters['diffrspfile'] = _request_value(diffrspfile, 'diffrspfile', 'file')

        if (self.parameters['scfile'] is None) or (not scfile is None):
            self.parameters['scfile'] = _request_value(scfile, 'scfile', 'file')

        if (self.parameters['ltcubefile'] is None) or (not ltcubefile is None):
            self.parameters['ltcubefile'] = _request_value(ltcubefile, 'ltcubefile', 'file')

        if (self.parameters['expmapfile'] is None) or (not expmapfile is None):
            self.parameters['expmapfile'] = _request_value(expmapfile, 'expmapfile', 'file')

        if (self.parameters['srcmdl'] is None) or (not srcmdl is None):
            self.parameters['srcmdl'] = _request_value(srcmdl, 'srcmdl', 'file')

        MyNewLike.unbinLikeCLI(eventFile=self.parameters['diffrspfile'],
                               scFile=self.parameters['scfile'],
                               expMap=self.parameters['expmapfile'],
                               expCube=self.parameters['ltcubefile'],
                               srcModel=self.parameters['srcmdl'],
                               irfs=self._irfs, **kwds)
